﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using SdmxMl.Common;
using SdmxMl.Manager;
using SdmxMl.Multiplexor;
using SdmxMl.Structure;
using Telerik.WinControls.UI;


namespace SakeVar
{
    /// <summary> Helper class for Grid representation of ConceptScheme </summary>
    public class GridCreator
    {

        #region Static Members

        /// <summary>Names of all columns for variable group</summary>
        public static  List<string> totListCol;
        /// <summary>Names of all columns for question group</summary>
        public static List<string> questListCol;

        #endregion

        #region Internal Members

        /// <summary>Grid control to use</summary>
        private RadGridView gridView;

        /// <summary>Concept Schema presented in grid</summary>
        private ConceptSchemeType cstConcepts;

        /// <summary>Used for SPSS report generation, marks first report element</summary>
        private bool firstSpssCode;

        /// <summary>Group and var type internalisation support</summary>
        private GroupVarHelper groupVar;

        #endregion

        #region Constructor

        /// <summary>Static C'tor</summary>
        static GridCreator()
        {
            totListCol = new List<string>(new string[] { "ACT", "APP", "ACS", "CHO", "NA" });
            questListCol = new List<string>(new string[] { "Q-ACT", "Q-APP", "Q-ACS", "Q-CHO", "Q-NA" });
        }

        /// <summary>The grid presentation of an ESPA concept Scheme </summary>
        /// <param name="radGrid">Grid control to use</param>
        /// <param name="cst">target concept schema</param>
        public GridCreator(RadGridView radGrid)
        {
            gridView = radGrid;

            // Load codelists for GROUP and INDIC_SUP translation
            SmManager sm = new SmManager();
            groupVar = new GroupVarHelper(
                sm.GetArtefact(SdmxArtefactType.CodeLists, new ArtefactRefBase(Mux.FullIdSmsAdapter("CH1_SAKE+CL_GROUP_PERS+1.0")), true) as CodeListType,
                sm.GetArtefact(SdmxArtefactType.CodeLists, new ArtefactRefBase(Mux.FullIdSmsAdapter("CH1_SAKE+CL_VAR_TYPE+1.0")), true) as CodeListType);

            // Set an event for a default tooltip that displays hidden text part
            gridView.ToolTipTextNeeded += new Telerik.WinControls.ToolTipTextNeededEventHandler(this.radGridView_ToolTipTextNeeded);

            // Conctruct grid columns
            SetView();
        }

        #endregion

        #region Grid Columns construction

        /// <summary> Constructs grid view Columns & grouping </summary>
        private void SetView()
        {
            gridView.BeginUpdate();

            gridView.MasterTemplate.Columns.Clear();
            gridView.EnableAlternatingRowColor = true;
            gridView.MasterTemplate.AllowColumnChooser = false;

            // Prepare column Thematique
            AddGroupColumn("Code");
            gridView.Columns["Code"].Width = 40;

            GridViewTextBoxColumn colText = new GridViewTextBoxColumn("Thema");
            colText.ReadOnly = true;
            colText.AllowResize = true;
            colText.AllowReorder = false;
            colText.Width = 100;
            gridView.MasterTemplate.Columns.Add(colText);

            foreach (string s in questListCol)
                AddQuestGroupColumn(s);

            AddGroupColumn("SUP");
            gridView.Columns["SUP"].Width = 50;

            colText = new GridViewTextBoxColumn("Text");
            colText.ReadOnly = true;
            colText.AllowResize = true;
            colText.AllowReorder = false;
            colText.Width = 150;
            gridView.MasterTemplate.Columns.Add(colText);

            foreach (string s in totListCol)
                AddGroupColumn(s);

            for (int i = 0; i < totListCol.Count; ++i)
            {
                gridView.Columns[totListCol[i]].HeaderText = groupVar.GetTranslationOfGroup(totListCol[i]);
                gridView.Columns[questListCol[i]].HeaderText = "Q-" + groupVar.GetTranslationOfGroup(totListCol[i]);
            }

            AddGroupColumn("Wave");
            gridView.Columns["Wave"].Width = 40;
            AddGroupColumn("Period");
            gridView.Columns["Period"].Width = 80;


            ColumnGroupsViewDefinition columnGroupsView;
            columnGroupsView = new ColumnGroupsViewDefinition();
            columnGroupsView.ColumnGroups.Add(new GridViewColumnGroup("THEMATIQUE"));
            columnGroupsView.ColumnGroups.Add(new GridViewColumnGroup("QUESTION by Person Group"));
            columnGroupsView.ColumnGroups.Add(new GridViewColumnGroup("INFORMATION"));
            columnGroupsView.ColumnGroups.Add(new GridViewColumnGroup("VARIABLE by Person Group"));
            columnGroupsView.ColumnGroups.Add(new GridViewColumnGroup("PERIODICITY"));
            for (int i = 0; i < 5; ++i)
                columnGroupsView.ColumnGroups[i].Rows.Add(new GridViewColumnGroupRow());

            columnGroupsView.ColumnGroups[0].Rows[0].Columns.Add(gridView.Columns["Code"]);
            columnGroupsView.ColumnGroups[0].Rows[0].Columns.Add(gridView.Columns["Thema"]);

            foreach (string s in questListCol)
                columnGroupsView.ColumnGroups[1].Rows[0].Columns.Add(gridView.Columns[s]);

            columnGroupsView.ColumnGroups[2].Rows[0].Columns.Add(gridView.Columns["SUP"]);
            columnGroupsView.ColumnGroups[2].Rows[0].Columns.Add(gridView.Columns["Text"]);

            foreach (string s in totListCol)
                columnGroupsView.ColumnGroups[3].Rows[0].Columns.Add(gridView.Columns[s]);

            columnGroupsView.ColumnGroups[4].Rows[0].Columns.Add(gridView.Columns["Wave"]);
            columnGroupsView.ColumnGroups[4].Rows[0].Columns.Add(gridView.Columns["Period"]);


            gridView.EndUpdate();

            gridView.ViewDefinition = columnGroupsView;
            gridView.AutoSizeColumnsMode = GridViewAutoSizeColumnsMode.None;

            gridView.AddNewRowPosition = SystemRowPosition.Top;
        }

        private void UpdateHeaders(bool isModule)
        {
            foreach (string s in totListCol)
            {
                gridView.MasterTemplate.Columns[s].HeaderText = isModule ? "MOD" : s;
                if (s != totListCol[0])
                    gridView.MasterTemplate.Columns[s].IsVisible = isModule == false;
            }

            foreach (string s in questListCol)
            {
                gridView.MasterTemplate.Columns[s].HeaderText = isModule ? "Q_MOD" : s;
                if (s != questListCol[0])
                    gridView.MasterTemplate.Columns[s].IsVisible = isModule == false;

            }

        }
        private void AddGroupColumn(string name)
        {
            GridViewTextBoxColumn colText = new GridViewTextBoxColumn(name);
            colText.ReadOnly = true;
            colText.Width = 68;
            colText.AllowReorder = false;
            colText.TextAlignment = System.Drawing.ContentAlignment.MiddleCenter;
            colText.AllowResize = false;
            gridView.MasterTemplate.Columns.Add(colText);
        }
        private void AddQuestGroupColumn(string name)
        {
            GridViewTextBoxColumn colText = new GridViewTextBoxColumn(name);
            colText.ReadOnly = true;
            colText.AllowReorder = false;
            colText.Width = 78;
            colText.TextAlignment = System.Drawing.ContentAlignment.MiddleLeft;
            colText.AllowResize = false;
            gridView.MasterTemplate.Columns.Add(colText);
        }

        #endregion

        #region Data binding

        public void LoadUnboundData(ConceptSchemeType cst, TreeNode nodeConcepts, bool isModule)
        {
            cstConcepts = cst;
            UpdateHeaders(isModule);
            Dictionary<string, ISdmxHierarchyListItem> dicoCodes = new Dictionary<string, ISdmxHierarchyListItem>();
            using (gridView.DeferRefresh())
            {
                gridView.MasterTemplate.Rows.Clear();

                // Each node of first level gives thematique
                foreach (TreeNode ndThema in nodeConcepts.Nodes)
                {
                    ConceptType cThema = ndThema.Tag as ConceptType;

                    if (cThema.Id.Length > 0) // == 1)
                    {
                        List<ISdmxHierarchyListItem> listVar = new List<ISdmxHierarchyListItem>();
                        // Second level is list of var, sometimes groupped
                        foreach (TreeNode ndVar in ndThema.Nodes)
                        {
                            ISdmxHierarchyListItem item = ndVar.Tag as ISdmxHierarchyListItem;
                            listVar.Add(item);
                        }

                        while (listVar.Count > 0)
                        {
                            ISdmxHierarchyListItem item = listVar[0];

                            // do we have var (s) with same text in list, same SUP indication and same Wave
                            List<ISdmxHierarchyListItem> listSame = new List<ISdmxHierarchyListItem>();
                            listSame.Add(item);
                            if (isModule == false)
                            {
                                for (int i = 1; i < listVar.Count; i++)
                                {
                                    string annual = string.Empty;
                                    if (listVar[i].Name.GetForLanguage("fr") == item.Name.GetForLanguage("fr") &&
                                        VarGroup.GetPeriod(listVar[i], ref annual) == VarGroup.GetPeriod(item, ref annual) &&
                                        VarGroup.GetSup(listVar[i]) == VarGroup.GetSup(item))
                                        listSame.Add(listVar[i]);
                                }
                            }

                            // Now set to varGroup
                            VarGroup vgr = new VarGroup(isModule);
                            foreach (ISdmxHierarchyListItem itFound in listSame)
                                vgr.Add(itFound);

                            // remove from varlist
                            foreach (ISdmxHierarchyListItem it in listSame)
                                listVar.Remove(it);
                            // Wave and periodicity
                            string periodicity = string.Empty;
                            string wave = vgr.GetWaveInfo(ref periodicity);

                            // Fill gridview
                            gridView.MasterTemplate.Rows.Add(cThema.Id, cThema.Name.ToString(), 
                                // Questions
                                vgr.GetQuestPrint("ACT"),
                                vgr.GetQuestPrint("APP"),
                                vgr.GetQuestPrint("ACS"),
                                vgr.GetQuestPrint("CHO"),
                                vgr.GetQuestPrint("NA"),
                                // INDIC_SUP
                                groupVar.GetTranslationOfVarTypeGroup(vgr.GetIndicSup()),

                                // Text
                                item.Name.ToString(),
                                // Var group
                                vgr.GetVarId("ACT"),
                                vgr.GetVarId("APP"),
                                vgr.GetVarId("ACS"),
                                vgr.GetVarId("CHO"),
                                vgr.GetVarId("NA"),
                                wave,
                                periodicity);
                        }
                    }
                }

            }
        }


        #endregion

        #region Export Var Labels

        /// <summary>Exports as text file the variables ID and text with INDIC_SUP and GROUP informations</summary>
        /// <param name="isForSAS">True SAS, False = SPSS</param>
        /// <returns></returns>
        public StringBuilder GetVarLabelExport(bool isForSAS)
        {
            StringBuilder sb = new StringBuilder();
            Dictionary<string, string> dicoVar = new Dictionary<string, string>();
            firstSpssCode = true;
          
            for (int i = 0; i < gridView.ChildRows.Count; i++)
            {
                SetBlocLabel(sb, gridView.ChildRows[i], dicoVar, isForSAS);
            }

            // SPSS needs final full stop
            if (isForSAS == false)
                FinalizeSpss(sb);

            return sb;
        }

        /// <summary>SPSS Final dot</summary>
        /// <param name="sb">export text container</param>
        private void FinalizeSpss(StringBuilder sb)
        {
             sb.Remove(sb.Length - 1, 1); sb.Append('.');
       }

        private void SetBlocLabel(StringBuilder sb, GridViewRowInfo rowInfo, Dictionary<string, string> dicoVar, bool isForSAS)
        {
            // Grouping used in grid view ?
            GridViewGroupRowInfo groupRow = rowInfo as GridViewGroupRowInfo;
            if (groupRow != null)
            {
                for (int i = 0; i < rowInfo.ChildRows.Count; i++)
                    SetBlocLabel(sb, rowInfo.ChildRows[i], dicoVar, isForSAS);
            }
            else
            {
                foreach (string s in totListCol)
                {
                    if (rowInfo.Cells[s].Value != null)
                    {
                        string variableId = rowInfo.Cells[s].Value.ToString();

                        if (variableId.Length > 0 && dicoVar.ContainsKey(variableId) == false)
                        {
                            ConceptType c = cstConcepts.ConceptList.LocateItem(variableId) as ConceptType;
                            // is it an aggregated concept ?
                            if (c.Id.IndexOf('-') > 0)
                            {
                                foreach (ConceptType cvar in cstConcepts.ConceptList)
                                {
                                    if (cvar.Parent == c.Id)
                                    {
                                        setVarLabel(cvar, sb, isForSAS);
                                    }
                                }
                            }
                            else
                            {
                                setVarLabel(c, sb, isForSAS);
                            }
                            dicoVar.Add(variableId, null);
                        }
                    }
                }
            }
       }

        private const string ANNOTATION_GROUP = "GROUP";
        private const string ANNOTATION_INDSUP = "INDIC_SUP";

        /// <summary>Construct variable ID and associated name with some info from annotations</summary>
        /// <param name="c">the concept representing the variable</param>
        /// <param name="sb">the container text builder</param>
        /// <param name="isSas">True if for SAS export, if false it is a SPSS export</param>
        private void setVarLabel(ConceptType c, StringBuilder sb, bool isSas)
        {
            int varPad = isSas ? 22 : 17;
            if (isSas == false && firstSpssCode)
            {
                sb.Append("VARIABLE LABELS").Append(Environment.NewLine);
                firstSpssCode = false;
            }

            sb.Append(c.Id).Append(' ', varPad - c.Id.Length);
            if (isSas)
                sb.Append('=');
            sb.Append("   \"");

            // Annotations transfer
            string indicSup = string.Empty;
            string grouping = string.Empty;
            Annotation aIndic = c.AnnotationList.GetAnnotationType(ANNOTATION_INDSUP);
            if (aIndic != null)
                indicSup = aIndic.Title + " - ";
            Annotation aGroup = c.AnnotationList.GetAnnotationType(ANNOTATION_GROUP);
            if (aGroup != null)
                grouping = "(" + aGroup.Title + ") ";

            sb.Append(indicSup).Append(grouping);
            sb.Append(c.Name.ToString()).Append("\"").Append(Environment.NewLine);
        }
        #endregion

        #region Data Input Export

        public DataTable GetInputTable()
        {
            DataTable tb = new DataTable();
            tb.Columns.Add(new DataColumn("Variable", typeof(string)));
            tb.Columns.Add(new DataColumn("Length", typeof(string)));
            tb.Columns.Add(new DataColumn("Dec", typeof(string)));
            tb.Columns.Add(new DataColumn("Calculated", typeof(string)));
            tb.Columns.Add(new DataColumn("Suggested", typeof(int)));

            Dictionary<string, string> dicoVar = new Dictionary<string, string>();

            for (int i = 0; i < gridView.ChildRows.Count; i++)
            {
                SetInputRow(tb, gridView.ChildRows[i], dicoVar);
            }

            return tb;
        }

        private void SetInputRow(DataTable tb, GridViewRowInfo rowInfo, Dictionary<string, string> dicoVar)
        {
            GridViewGroupRowInfo groupRow = rowInfo as GridViewGroupRowInfo;
            if (groupRow != null)
            {
                for (int i = 0; i < rowInfo.ChildRows.Count; i++)
                    SetInputRow(tb, rowInfo.ChildRows[i], dicoVar);
            }
            else
            {
                foreach (string s in totListCol)
                {
                    if (rowInfo.Cells[s].Value != null)
                    {
                        string variableId = rowInfo.Cells[s].Value.ToString();

                        if (variableId.Length > 0 && dicoVar.ContainsKey(variableId) == false)
                        {
                            ConceptType c = cstConcepts.ConceptList.LocateItem(variableId) as ConceptType;
                            // is it an aggregated concept ?
                            if (c.Id.IndexOf('-') > 0)
                            {
                                foreach (ConceptType cvar in cstConcepts.ConceptList)
                                {
                                    if (cvar.Parent == c.Id)
                                    {
                                        string dec = cvar.TextFormat != null ? cvar.TextFormat.decimals : string.Empty;
                                        string len = cvar.TextFormat != null ? cvar.TextFormat.maxLength : "0";
                                        tb.Rows.Add(new object[] { cvar.Id, len, dec, string.Empty, null });
                                    }
                                }
                            }
                            else
                            {
                                string dec = c.TextFormat != null ? c.TextFormat.decimals : string.Empty;
                                string len = c.TextFormat != null ? c.TextFormat.maxLength : "0";
                                tb.Rows.Add(new object[] { c.Id, len, dec, string.Empty, null });
                            }
                            dicoVar.Add(variableId, null);
                        }
                    }
                }
            }
        }

        #endregion

        #region Export Format

        public StringBuilder GetFormatExport(bool isForSAS)
        {
            StringBuilder sb = new StringBuilder();
            Dictionary<string, string> dicoVar = new Dictionary<string, string>();
            Dictionary<string, ReferenceFullCodes> dicoRef = new Dictionary<string, ReferenceFullCodes>();

            // Fetch availables codelists
            SmManager sm = ReferenceManager.GetSmContainerImage();
            firstSpssCode = true;

            for (int i = 0; i < gridView.ChildRows.Count; i++)
            {
                SetBlocCode(sm, sb, gridView.ChildRows[i], dicoVar, isForSAS);
            }

            // SPSS needs final full stop
            if (isForSAS == false)
                FinalizeSpss(sb);

            return sb;
        }

        private void SetBlocCode(SmManager sm, StringBuilder sb, GridViewRowInfo rowInfo, Dictionary<string, string> dicoVar, bool isForSAS)
        {
            GridViewGroupRowInfo groupRow = rowInfo as GridViewGroupRowInfo;
            if (groupRow != null)
            {
                for (int i = 0; i < rowInfo.ChildRows.Count; i++)
                    SetBlocCode(sm, sb, rowInfo.ChildRows[i], dicoVar, isForSAS);
            }
            else
            {
                foreach (string s in totListCol)
                {
                    if (rowInfo.Cells[s].Value != null)
                    {
                        string variableId = rowInfo.Cells[s].Value.ToString();

                        if (variableId.Length > 0 && dicoVar.ContainsKey(variableId) == false)
                        {
                            ConceptType c = cstConcepts.ConceptList.LocateItem(variableId) as ConceptType;
                            // is it an aggregated concept ?
                            if (c.Id.IndexOf('-') > 0)
                            {
                                foreach (ConceptType cvar in cstConcepts.ConceptList)
                                {
                                    if (cvar.Parent == c.Id)
                                    {
                                        GenCodes(sb, sm, cvar, isForSAS);
                                    }
                                }
                            }
                            else
                            {
                                GenCodes(sb, sm, c, isForSAS);
                            }
                            dicoVar.Add(variableId, null);
                            firstSpssCode = false;
                        }
                    }
                }
            }
        }


        private void GenCodes(StringBuilder sb, SmManager sm, ConceptType cvar, bool isForSAS)
        {
            ConceptType conceptQuality = cstConcepts.ConceptList.LocateItem(cvar.Id + "_F") as ConceptType;
            if (conceptQuality != null)
                if (conceptQuality.Parent == null || conceptQuality.Parent != cvar.Id)
                    conceptQuality = null;

            CodeListType cltCodes = null;
            CodeListType cltQuality = null;
            if (cvar.CoreRepresentation.IsFilled)
                cltCodes = sm.GetArtefact(SdmxArtefactType.CodeLists, cvar.CoreRepresentation, true) as CodeListType;
            if (conceptQuality != null && conceptQuality.CoreRepresentation.IsFilled)
                cltQuality = sm.GetArtefact(SdmxArtefactType.CodeLists, conceptQuality.CoreRepresentation, true) as CodeListType;

            ReferenceFullCodes rfc = new ReferenceFullCodes(cltCodes, cltQuality, cvar.Id);
            rfc.FillCodes(sb, cvar.Id, isForSAS, firstSpssCode);
        }

        #endregion

        #region Tooltip Event

        private void radGridView_ToolTipTextNeeded(object sender, Telerik.WinControls.ToolTipTextNeededEventArgs e)
        {
            GridDataCellElement dataCell = sender as GridDataCellElement;
            GridHeaderCellElement headerCell = sender as GridHeaderCellElement;
            if (dataCell != null)
            {
                if (dataCell.ColumnInfo.Name == "SUP" && dataCell.Value != null)
                {
                    e.ToolTipText = groupVar.GetTooltypOfVartype(dataCell.Value.ToString());
                }
                else
                {
                    TextPart textPart = new TextPart(dataCell);
                    SizeF size = textPart.Measure(new SizeF(float.PositiveInfinity, float.PositiveInfinity));
                    SizeF sizeInCell = textPart.Measure(new SizeF(dataCell.ColumnInfo.Width, float.PositiveInfinity));

                    string toolTipText = null;

                    float cellWidth = dataCell.ColumnInfo.Width - dataCell.BorderWidth * 2;
                    float cellHeight = dataCell.Size.Height - dataCell.BorderWidth * 2;

                    if (size.Width > cellWidth || cellHeight < sizeInCell.Height)
                    {
                        toolTipText = dataCell.Text;
                    }

                    e.ToolTipText = toolTipText;
                }
            }

            else if (headerCell != null)
            {
                string s = headerCell.Value.ToString().Replace("Q-", "");

                if (s == "SUP")
                    e.ToolTipText = groupVar.GetVarTypeTooltip();
                else
                {
                    //locate column with same header text
                    foreach (GridViewColumn col in gridView.Columns)
                    {
                        if (col.HeaderText == s)
                        {
                            e.ToolTipText = groupVar.GetTextOfGroup(col.Name);
                            break;
                        }
                    }
                }
            }
        }
        #endregion
    }

    #region Class ReferenceFullCodes

    /// <summary>
    /// Helper class hold codelist and quality codelist attached to variable ID
    /// Class offers a method for generating code formats instructions
    /// </summary>
    public class ReferenceFullCodes
    {
        public CodeListType CltCodes { get; private set; }
        public CodeListType CltQuality { get; private set; }
        public string FormatId { get; private set; }

        public ReferenceFullCodes(CodeListType codes, CodeListType quality, string id)
        {
            CltCodes = codes;
            CltQuality = quality;
            FormatId = id;
        }

        /// <summary>Append in container the text for code Format instructions </summary>
        /// <param name="sb">The string container</param>
        /// <param name="id">Override FormatId value if set</param>
        /// <param name="isForSAS">True = SAS, False = SPSS</param>
        /// <param name="firstSpssCode">First creation row if true</param>
        public void FillCodes(StringBuilder sb, string id, bool isForSAS, bool firstSpssCode)
        {
            char lang = LangSupport.CurLang.ToUpper()[0];
            if (!IsEmpty)
            {
                // Set Id
                int varPad = 29;
                if (string.IsNullOrEmpty(id))
                    id = FormatId;
                if (isForSAS)
                    sb.Append(";value ");
                else
                {
                    if (firstSpssCode)
                        sb.Append("VALUE LABELS").Append(Environment.NewLine);
                    else
                        sb.Append("/");
                }
                sb.Append(id);
                if (isForSAS)
                    sb.Append(lang);
                sb.Append(' ', varPad - id.Length);

               bool firstCode = true;
               if (CltCodes != null)
                {
                    foreach (CodeType c in CltCodes.CodeList)
                    {
                        if (firstCode == false)
                            sb.Append(' ', 40);
                        sb.Append(c.Id);
                        if (isForSAS)
                            sb.Append(" =  \"  ").Append(c.Id).Append('.').Append(c.Name.ToString()).Append('\"').Append(Environment.NewLine);
                        else
                            sb.Append("         \"  ").Append(c.Id).Append('.').Append(c.Name.ToString()).Append('\"').Append(Environment.NewLine);
                        firstCode = false;
                    }
                }
                if (CltQuality != null)
                {
                    foreach (CodeType c in CltQuality.CodeList)
                    {
                        if (firstCode == false)
                            sb.Append(' ', 40);
                        sb.Append(c.Id);
                        if (isForSAS)
                            sb.Append(" =  \"  ").Append(c.Id).Append('.').Append(c.Name.ToString()).Append('\"').Append(Environment.NewLine);
                        else
                            sb.Append("         \"  ").Append(c.Id).Append('.').Append(c.Name.ToString()).Append('\"').Append(Environment.NewLine);
                        firstCode = false;
                    }
                }
            }
        }

        #region Properties

        /// <summary>Empty if codelist and quality codelist not affected </summary>
        public bool IsEmpty
        {
            get { return CltCodes == null && CltQuality == null; }
        }
        #endregion
    }
    #endregion
}
